import string, types

class Object(object):
    def __init__(self, names, articles = ("a", "the")):
        """'names' should be an list containing all variations on the object's name, with the primary entry first.
        'articles' should be a list of two articles, an article for object when unknown("you see a cookie") and one
        for the object when known("you dropped the cookie")"""
        if type(names) in types.StringTypes:
            self.names = []
            self.names.append(names)
        else:
            self.names = list(names)
        self.container = None
        self.unknownArticle = articles[0]
        self.knownArticle = articles[1]
    def nameAsUnknown(self):
        s = self.unknownArticle
        if s: s += " "
        return s + self.names[0]
    def nameAsKnown(self):
        s = self.knownArticle
        if s: s += " "
        return s + self.names[0]
    def __str__(self):
        return self.names[0]
objects = []
accessibleObjects = []
statusEntries = []

class Me(object):
    def __init__(self):
        pass
me = Me()

commands = []

STARTUP_CALL = 0
PREPROCESS_CALL = 1
POSTPROCESS_CALL = 2
hooks = []

#method transplant code for adding methods to instances
class transplant:
   def __init__(self, method, host, method_name=None):
      self.host = host
      self.method = method
      setattr(host, method_name or method.__name__, self)
   def __call__(self, *args, **kwargs):
      nargs = [self.host]
      nargs.extend(args)
      return apply(self.method, nargs, kwargs)

def init():
    ret = []
    for hook in hooks:
        ret2 = hook.hook(STARTUP_CALL, None, ret)
        if ret2:
            ret.append(ret2)
    return string.join(ret, "\n\n")

class GameError(Exception):
    def __init__(self, value): self.value = value
    def __str__(self): return self.value
class MatchError(GameError):
    def __init__(self, value): self.value = value
    def __str__(self): return self.value
class NoMatchError(MatchError):
    def __init__(self, value): self.value = value
    def __str__(self): return self.value
class MultipleMatchesError(MatchError):
    def __init__(self, value): self.value = value
    def __str__(self): return self.value

def getItem(name, container = accessibleObjects):
    matches = []
    for o in container:
        if name in o.names: matches.append(o)
    if not len(matches):
        raise NoMatchError("You see nothing like that here.")
    if len(matches) > 1:
        s = "Do you mean " \
                + smartJoin([a.nameAsKnown() for a in matches], "or") + "?"
        raise MultipleMatchesError(s)
    return matches[0]

def smartJoin(lst, conj = "and"):
    """Joins a list together in a grammatically-correct way, as in "a, b, c, and d"."""
    if len(lst) == 0:
        return ""
    elif len(lst) == 1:
        return str(lst[0])
    elif len(lst) == 2:
        return str(lst[0]) + " " + conj + " " + str(lst[1])
    else:
        s = str(lst[0])
        for a in xrange(1, len(lst)-1):
            s += ", " + str(lst[a])
        s += ", " + conj + " " + str(lst[-1])
        return s

def process(commandtext):
    ret = []
    for hook in hooks:
        ret2 = hook.hook(PREPROCESS_CALL, commandtext, ret)
        if ret2:
            ret.append(ret2)
    try:
        for command in commands:
            ret2 = command.process(commandtext)
            if ret2:
                ret.append(ret2)
                break
    except GameError, e:
        ret.append(e.value)
    for hook in hooks:
        ret2 = hook.hook(POSTPROCESS_CALL, commandtext, ret)
        if ret2:
            ret.append(ret2)
    return string.join(ret, "\n")

